#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include "select.h"
#include "1mysql.h"
 
#define SER_IP       "127.0.0.1"
#define SER_PORT     6789
 
#define LISTEN_MAX   100
NowClient user[NOW_MAX];    //表示当前在线人的资料
 
 
/********************************************
 * TCP通信服务器端流程
 * 1，创建套接字
 * 2，绑定
 * 3，监听
 * 4，等待连接
 * 5，收发数据
 * 6，关闭套接字
 * *************************************************/
 
int all_clifd[256] = {0};//存放所有的客户端描述符
int count = 0;//计数
MYSQL *my_sql;//数据库描述符
 
void accept_do_handler(int sockfd);
void recv_do_handler(int sockfd);
void send_to_all_cli(char *buf);
 
int sockfd;
void guan()//关闭函数
{
    close(sockfd);
    mysql_close(my_sql);
    exit(-1);
}
int main(int argc, char  *argv[])
{
   pid_t pid= fork();
   if (pid==0){
    //1.创建套接字   IPV4  流式套接字  TCP协议
     sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd == -1)
    {
        perror("socket");
        return -1;
    }
    printf("sockfd = %d\n",sockfd);
    signal(10,guan);
    //2.绑定
    struct sockaddr_in seraddr;
    memset(&seraddr,0,sizeof(struct sockaddr_in));
    seraddr.sin_family = AF_INET;//ipv4协议类型
    // seraddr.sin_addr;//ip地址  网络字节序
    // inet_aton(SER_IP,&seraddr.sin_addr);
    seraddr.sin_addr.s_addr = inet_addr(SER_IP);
    seraddr.sin_port = htons(SER_PORT);//端口    网络字节序
    int opt=1;
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    if(bind(sockfd,(struct sockaddr *)&seraddr,sizeof(struct sockaddr_in)) == -1)
    {
        perror("bind");
        return -1;
    }
    printf("绑定成功\n");
 
    //3.监听
    if(listen(sockfd,LISTEN_MAX) == -1)
    {
        perror("listen");
        return -1;
    }
    printf("监听成功\n");
 
    //非阻塞轮询
    int maxfd = sockfd;
    //处理连接请求--》sockfd发生可读变化
    //接收数据--》clifd发生可读变化
    fd_set readfds;//检测可读变化
     my_sql=Sconnect();//连接数据库
    int ret = 0;
    // struct timeval select_time;
    int i = 0;
    while(1)
    {
        //
        //先去清空集合
        FD_ZERO(&readfds);
        //再去添加
        FD_SET(sockfd,&readfds);
        
        for(i = 0;i<count;i++)
        {
            //添加描sockfd述符
            FD_SET(all_clifd[i],&readfds);
            //求出来最大的
            if(all_clifd[i]>maxfd)
            {
                maxfd = all_clifd[i];
            }
           
        }
        printf("\n");
        
        //永久阻塞
        ret = select(maxfd+1,&readfds,NULL,NULL,NULL);
        if(ret == -1)
        {
            perror("select");
            return -1;
        }
        else if(ret == 0)
        {
            printf("超时\n");
            continue;
        }
        //有变化，会打断阻塞，将集合里边没有变化的删除掉
        else
        {
            // printf("有文件描述符发生变化\n");
            //判断是谁发生的变化，根据变化去作处理
            //轮询从0到maxfd所有的文件描述符
            for(i = 0;i<=maxfd;i++)
            {
                //检查发生变化的是否是readfds集合里边的
                if(FD_ISSET(i,&readfds)==1)
                {
                    //服务器套接字描述符发生变化
                    //有客户端请求连接
                    if(i == sockfd)
                    {
                        accept_do_handler(sockfd);
                    }
                    //有客户端发消息
                    else
                    {
                        
                        recv_do_handler(i);
                    }
                }
            } 
        }
        
    }
    
 
   }
   else
   {
    char a;
        while(1)
        {
            printf("y/n\n");
            a=getchar();
            if(a=='y')
            {
                printf("1");
                kill(pid,10);
                exit(0);
            }
        }
   }
}
 
void accept_do_handler(int sockfd)
{
    //4.等待连接
    //存放连接成功之后，客户端的IP地址和端口
    struct sockaddr_in cliaddr;
    memset(&cliaddr,0,sizeof(struct sockaddr_in));
    socklen_t len = sizeof(struct sockaddr_in);
    int clifd = accept(sockfd,(struct sockaddr *)&cliaddr,&len);
    if(clifd == -1)
    {
        perror("accept");
        return;
    }
   
 
    //存储所有的客户端描述符
    all_clifd[count] = clifd;
    count++;
    
}
 
void recv_do_handler(int client_socket)
{
    // 5.数据收发
    
    int i = 0;
    int ret = 0;
    int pos = 0;//记录下表
    Msg msg;
    memset(&msg,0,sizeof(msg));
    
    //阻塞
    ret = recv(client_socket,&msg,sizeof(msg),0);
    char shi[1024];
    sprintf(shi,"%s %s %s %s",msg.fromname,msg.account,msg.signname,msg.password);
    printf("%s\n",shi);
    if(ret==-1)
    {
        perror("recv");
        return ;
    }
    else if(ret == 0)
    {
        
        for(i = 0;i<count;i++)
        {
            if(all_clifd[i] == client_socket)
            {
                all_clifd[i] = 0;
                pos = i;
                break;
            }
        }
        for(i = pos;i<count-1;i++)
        {
            all_clifd[i] = all_clifd[i+1];
        }
        all_clifd[count-1]=0;
        count--;
        
    }
    else
    {
        
        switch (msg.cmd)
		{
			case 1 :    // 注册
				regiss(client_socket, &msg);
				break;
			case 2 :    //登录
				ret = entry(client_socket, &msg);
				send(client_socket , &msg ,sizeof(Msg),0);
				if (ret == 1)
				{
					//在线人数加1
					int i;
					for (i=0; i<NOW_MAX; i++)
					{
						if(user[i].socket == 0)
						{
							strcpy(user[i].name, msg.fromname);
							user[i].socket = client_socket;
                            user[i].jin=0;
                            user[i].ti=0;
							printf("客户端在线人数加一\n");
							break;
						}
					}
                    char guanbuf[1000];
                    sprintf(msg.msg,"%s已登录",msg.fromname);
                    msg.cmd=22;
					//用户界面
					
                    for(i=0;i<NOW_MAX;i++)
                    {
                        if(user[i].socket == 0)
                        continue;
                        if(user[i].socket==client_socket)
                        continue;
                        send(user[i].socket,&msg,sizeof(Msg),0);
                    }  
				}	
				break;
                case 3://群聊
                server_chatall(client_socket, &msg);
                server_save(&msg);
				break;
                case 4 :    //私聊
				server_chatone(client_socket, &msg);
                server_save(&msg);	
				break;
                case 5://退出登录
                server_entryout(client_socket,&msg);
                sprintf(msg.msg,"%s已下线",msg.fromname);
                    msg.cmd=22;
                    for(i=0;i<NOW_MAX;i++)
                    {
                        if(user[i].socket == 0)
                        continue;
                        if(user[i].socket==client_socket)
                        continue;
                        send(user[i].socket,&msg,sizeof(Msg),0);
                    }  break;
                case 6://在线人数
                see_nowuser(client_socket,&msg);break;
                case 7://改签名
                server_revise_sign(client_socket, &msg);
				break;
                case 8://改密码
                server_revise_password(client_socket, &msg);
				break;
                case 9://传输文件
                server_transfer_file(client_socket, &msg);
                    break;
                case 10:   //接受传输文件
                    server_transfer_file_y(&msg);
                    break;
                case -10 : //拒绝传输文件
                    server_transfer_file_n(&msg);
                    break;
                case 11:   //一切条件都已成立,直接开始传输
				server_start_transfer_file(&msg);break;
                case 87://禁言
                shutup(client_socket,&msg);break;
                case 88://移除
                sremove(client_socket,&msg);break;
                case 99://注销
                server_logout(client_socket,&msg);break;
            
		}
    }
    
}
 
 
 
int regiss(int client_socket, Msg *msg)//注册
{
    //printf("nihaos%d\n",__LINE__);
    
    int flag;
    int i=-1,j=-1,a=0;
    i=search(my_sql,msg);
    j=search1(my_sql);
    if(j==0)
    {
        a=1;
    }
    printf("%d\n",i);
    if(i==0)
    { //printf("nihaos%d\n",__LINE__);
        flag= data_insert(my_sql,msg,client_socket,a);
        msg->cmd+=1000;
    }
    else if(i==1)
    msg->cmd=-1;
    printf("%d\n",msg->cmd);
   // printf("nihaos%d\n",__LINE__);
    send(client_socket,msg,sizeof(Msg),0);
    
}
 
int entry(int client_socket, Msg *msg)
{
	printf(" %s进行登录\n",msg->fromname);
	
	//用户登录
	
	
	int flag = Entry_User(my_sql,msg,client_socket);
    if (flag == -1)
	{
		printf("登录失败,系统繁忙\n");
		msg->cmd = -1;
		return -1;
	}
	
	if (flag == -2)
	{
		printf("登录失败,用户名不存在\n");
		msg->cmd = -2;
		return -2;
	}
	if (flag == -3)
	{
		printf("登录失败,用户已登录\n");
		msg->cmd = -3;
		return -3;
	}
    if (flag == -4)
	{
		printf("登录失败,密码错误\n");
		msg->cmd = -4;
		return -4;
	}
	else 
	{
		printf("登录成功\n");
		msg->cmd += 1000;
		return 1;
	}
}
 
void server_entryout(int client_socket,Msg *msg)//退出登录
{
   int ret= change(my_sql,"type","0",msg->account);
   //printf("%d\n",client_socket);
   if(ret==0)
   {
    int i ;
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket == client_socket)
		{
			user[i].socket = 0;
			printf("客户端在线人数减一\n");
			break;
		}
	}
   msg->cmd=5;
   sprintf(msg->msg,"退出登录成功");
   }
   else
   {
   sprintf(msg->msg,"退出登录失败");
   msg->cmd=-5;
   }
   //printf("%d\n",client_socket);
   char shi [1024];
   sprintf(shi,"%s %s %s %s %d",msg->fromname,msg->account,msg->signname,msg->password,msg->cmd);
    printf("%s\n",shi);
   send(client_socket,msg,sizeof(Msg),0);
   //printf("fasongcg\n");
}
 
void server_chatall(int client_socket, Msg * msg)//群聊
{
	printf (" %s 进行群发.\n",msg->fromname);
	
    int j;
        for (j=0; j<NOW_MAX; j++)
        {
            if(user[j].socket==client_socket)
            {
                if(user[j].ti==1)
                {
                    memset(msg->msg,0,1000);
                    sprintf(msg->msg,"发送失败已被移除群聊");
                    write(client_socket, msg , sizeof(Msg));
                    return ;
                }
                if(user[j].jin==1)
                {
                    memset(msg->msg,0,1000);
                    sprintf(msg->msg,"发送失败已被禁言");
                    write(client_socket, msg , sizeof(Msg));
                    return ;
                }
                
                break;
            }
		}
	int i;
	for(i=0; i<NOW_MAX; i++)
	{
		if (user[i].socket != 0&&user[i].socket!=client_socket&&user[i].ti!=1)
		{
			write(user[i].socket, msg , sizeof(Msg));
		}
	}
}
 
void server_chatone(int client_socket, Msg * msg)//私聊
{
	printf ("私聊 %s发送信息给%s\n",msg->fromname,msg->localname);
	
	int i;
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
		{
			write(user[i].socket, msg , sizeof(Msg));
			printf("私聊成功\n");
			break;
		}
	}
	if (i == NOW_MAX)
	{
		msg->cmd = -3;    //表示私聊失败
		write(client_socket, msg , sizeof(Msg));
		printf("私聊失败\n");
	}
}
 
void server_logout(int client_socket,Msg *msg)//注销
{
    int ret=mysql_delete(my_sql,msg->account);
    if(ret==0)
    {
        printf("注销成功\n");
        int i ;
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket == client_socket)
		{
			user[i].socket = 0;
			printf("客户端在线人数减一\n");
			break;
		}
	}
        msg->cmd=99;
    }
    else
    msg->cmd=-99;
    send(client_socket,msg,sizeof(Msg),0);
}
 
void server_save(Msg*msg)//保存聊天记录
{
	char buf2[120];
	char ai[1250];
	memset(buf2,0,sizeof(buf2));
	memset(ai,0,sizeof(ai));
	
	sprintf(buf2,"../serverliaotianjilu/all.txt");
	
	
	int ps=open(buf2,O_WRONLY|O_CREAT|O_APPEND,0644);
	sprintf(ai,"%s->%s:%s\n",msg->fromname,msg->localname,msg->msg);
	write(ps,ai,strlen(ai));
	close (ps);
 
}
 
void see_nowuser(int client_socket, Msg * msg)//在线人员
{
	printf("%s 查看当前在线人员\n",msg->fromname);
	
	int i;
	int len;
	char buf[1024] = {0};
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket != 0)
		{
			strcat(buf,user[i].name);
			len = strlen(buf);
			buf[len] = ' ';
		}
	}
	strcpy(msg->msg,buf);
	
	write(client_socket, msg, sizeof(Msg));
	printf("查看成功\n");
}
 
 
void server_revise_sign(int client_socket, Msg * msg)//修改个性签名
{
	printf("%s 修改个性签名\n",msg->fromname);
	int ret = change(my_sql,"signname",msg->signname,msg->account);    //修改数据库
	if (ret == -1)
	{
		msg->cmd = -7;
		printf("%s 修改个性签名失败\n",msg->fromname);
		write(client_socket, msg, sizeof(Msg));
	}
	printf("%s 修改个性签名成功\n",msg->fromname);
	write(client_socket, msg, sizeof(Msg));
}
 
//修改密码
void server_revise_password(int client_socket, Msg * msg)
{
	printf("%s 修改密码\n",msg->fromname);
	int ret = change(my_sql,"passwd",msg->password,msg->account);
	if (ret == -1)
	{
		msg->cmd = -8;
		printf("%s 修改密码失败\n",msg->fromname);
		write(client_socket, msg, sizeof(Msg));
	}
	printf("%s 修改密码成功\n",msg->fromname);
	write(client_socket, msg, sizeof(Msg));
}
 
void shutup(int client_socket, Msg * msg)//禁言
{
        int i;
        for (i=0; i<NOW_MAX; i++)
        {
            if(strcmp(user[i].name ,msg->localname)==0)
            {
                
                user[i].jin=1;
                
                
                break;
            }
		}
        write(client_socket, msg, sizeof(Msg));
}
 
void sremove(int client_socket, Msg * msg)//移除
{
        int i;
        for (i=0; i<NOW_MAX; i++)
        {
            if(strcmp(user[i].name ,msg->localname)==0)
            {
                
                user[i].ti=1;
                
                
                break;
            }
		}
        write(client_socket, msg, sizeof(Msg));
}
 
//传输文件
void server_transfer_file(int client_socket, Msg * msg)
{
	printf ("%s 请求发送%s文件给 %s\n",msg->fromname,msg->signname,msg->localname);
	int i;
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
		{
			write(user[i].socket, msg , sizeof(Msg));
			printf("发送给%s信息进行判断是否接受\n",msg->localname);
			break;
		}
	}
	if (i == NOW_MAX)
	{
		msg->cmd == -9;    //表示传输文件失败
		write(client_socket, msg , sizeof(Msg));
		printf("发送文件失败,好友不在线或不存在\n");
	}
}
 
void server_transfer_file_y(Msg * msg)//接收文件
{
	printf("%s 接受文件传输.\n",msg->fromname);
	
	int i;
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
		{
			write(user[i].socket, msg , sizeof(Msg));
			break;
		}
	}
}
 
//拒绝文件
void server_transfer_file_n(Msg * msg)
{
	printf("%s 不愿意接受文件传输.\n",msg->fromname);
	
	int i;
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
		{
			write(user[i].socket, msg , sizeof(Msg));
			break;
		}
	}
}
 
void server_start_transfer_file(Msg * msg)
{
	//printf("文件传输中\n");
	int i;
	for(i=0; i<NOW_MAX; i++)
	{
		if(user[i].socket != 0 && strcmp(user[i].name, msg->localname) == 0)
		{
			write(user[i].socket, msg , sizeof(Msg));    //写文件数据
			break;
		}
	}
	if(msg->num != 1024)
	{
		printf("文件传输完成\n");
	}
}